package com.xw.echo.core.cluster.fault;
import com.xw.echo.core.cluster.Cluster;
import com.xw.echo.core.protocol.MethodInvokData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

/**
 * FallRetry类实现了ClusterFault接口，用于在集群中进行故障转移和重试操作
 * 它通过在遇到故障时重试指定次数来提高系统的容错性
 */
public class FallRetry implements ClusterFault{
    private static final Logger log = LoggerFactory.getLogger(FallRetry.class);
    private Cluster cluster;
    private int retryCount = 0;
    private final int maxRetry; // 最大重试次数

    /**
     * 构造方法，初始化FallRetry对象
     *
     * @param cluster 集群对象，用于执行调用
     * @param maxRetry 最大重试次数
     */
    public FallRetry(Cluster cluster, int maxRetry) {
        this.cluster = cluster;
        this.maxRetry = maxRetry;
    }

    /**
     * 执行调用操作，当调用失败时进行重试
     *
     * @param hostAndPort 远程主机和端口
     * @param methodInvokData 方法调用数据
     * @param method 要调用的方法
     * @return 调用结果，如果调用失败且重试次数未超过最大值，则进行重试
     */
    @Override
    public Object invoker(String hostAndPort, MethodInvokData methodInvokData, Method method) {
        log.info("开始执行调用，hostAndPort: " + hostAndPort + ", method: " + method.getName());
        try {
            Object invoke = cluster.invoke(hostAndPort, methodInvokData, method);
            if (invoke instanceof Future<?>) {
                // 异步调用，使用CompletableFuture处理结果
                return ((CompletableFuture<?>) invoke).whenComplete((r, e) -> {
                    if (e != null) {
                        // 异步调用失败，进行重试
                        retry(hostAndPort, methodInvokData, method);
                    }
                });
            } else {
                // 同步调用，直接返回结果
                return invoke;
            }
        } catch (Exception e) {
            // 同步调用失败，进行重试
            return retry(hostAndPort, methodInvokData, method);
        }
    }

    /**
     * 重试方法，当调用失败时尝试重新调用
     *
     * @param hostAndPort 远程主机和端口
     * @param methodInvokData 方法调用数据
     * @param method 要调用的方法
     * @return 重试结果，如果重试次数超过最大值，则返回null
     */
    private Object retry(String hostAndPort, MethodInvokData methodInvokData, Method method) {
        if (retryCount < maxRetry) {
            retryCount++;
            System.out.println("重试第 " + retryCount + " 次，hostAndPort: " + hostAndPort + ", method: " + method.getName());
            // 递归调用invoker方法进行重试
            return invoker(hostAndPort, methodInvokData, method);
        } else {
            System.out.println("达到最大重试次数，放弃重试，hostAndPort: " + hostAndPort + ", method: " + method.getName());
            // 可以抛出异常或者返回默认值，这里返回null
            return null;
        }
    }
}

